package checker

import (
	"kumachan/interpreter/lang/textual/ast"
	"kumachan/interpreter/lang/common/source"
	"kumachan/interpreter/compiler/loader"
	"kumachan/interpreter/compiler/loader/manifest"
)


type ModuleInfo struct {
	ModName      string
	ModImported  map[string] *ModuleInfo
	ModManifest  manifest.Module
}
type ModuleInfoCollection (map[string] *ModuleInfo)
func (mic ModuleInfoCollection) GetTypeModuleInfo(def TypeDef) *ModuleInfo {
	var info, exists = mic[def.Name.ModuleName]
	if exists {
		return info
	} else {
		panic("something went wrong")
	}
}
func (mic ModuleInfoCollection) GetFunctionModuleInfo(f *Function) *ModuleInfo {
	var info, exists = mic[f.Name.ModuleName]
	if exists {
		return info
	} else {
		panic("something went wrong")
	}
}

func collectModuleInfo(entry *loader.Module) (*ModuleInfo, ModuleInfoCollection) {
	var mic = make(ModuleInfoCollection)
	var entry_info = registerModuleInfo(entry, mic)
	return entry_info, mic
}
func registerModuleInfo(mod *loader.Module, mic ModuleInfoCollection) *ModuleInfo {
	var mod_name = mod.Name
	var existing, exists = mic[mod_name]
	if exists {
		return existing
	}
	var all_imported = make(map[string] *ModuleInfo)
	for preferred_name, imported := range mod.ImpMap {
		all_imported[preferred_name] = registerModuleInfo(imported, mic)
	}
	var mi = &ModuleInfo {
		ModName:     mod_name,
		ModImported: all_imported,
		ModManifest: mod.Manifest,
	}
	mic[mod_name] = mi
	return mi
}


type ModuleVisitedSet (map[*loader.Module] struct{})
func (mvs ModuleVisitedSet) Visited(mod *loader.Module) bool {
	var _, visited = mvs[mod]
	if !(visited) {
		mvs[mod] = struct{}{}
	}
	return visited
}

func traverseStatements (
	mod  *loader.Module,
	mic  ModuleInfoCollection,
	mvs  ModuleVisitedSet,
	f    func(stmt ast.VariousStatement, mi *ModuleInfo) *source.Error,
) source.Errors {
	if mvs.Visited(mod) {
		return nil
	}
	{ var err = mod.ForEachImported(func(imported *loader.Module) source.Errors {
		return traverseStatements(imported, mic, mvs, f)
	})
	if err != nil {
		return err
	} }
	var errs source.Errors
	var mi = mic[mod.Name]
	for _, stmt := range mod.AST.Statements {
		source.ErrorsJoin(&errs, f(stmt, mi))
	}
	return errs
}


